package edu.mercer.sse.bkp;

import java.util.Iterator;
import java.util.List;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;

/**
 * Concrete class as in Command Pattern.<p>
 *
 * Composite: Leaf-container as in Composite Pattern.<p>
 * 
 * @see CompressCommand
 */

public class CompressCommandComposite implements 
				   
	CommandComposite<CompressCommand, 
					 CompressCommandComposite, 
					 ImmutableList<?>>,
					 					
	CommandRecall<ImmutableList<?>, Boolean>, 
							
	CommandProgress<ImmutableList<?>, Float>
{
	/**
	 * List&#60;CompressCommand&#62;<p>
	 * 
	 * Collection of CompressCommand as in Composite/Parent design.<p>
	 */
	
	private static List<CompressCommand> _commands = Lists.newArrayList();

	/**
	 * List&#60;ListenableFuture&#62; results of execute()<p>
	 * 
	 * Collection of Resulting ListenableFuture's to return from this 
	 * 	Composite execute.<p>
	 */
	
	private static List<ListenableFuture
							<TaskResults<ImmutableMap<String, String>>>> 
		_futures = Lists.newArrayList();


	/** No-arg constructor for use by factory.<p> */
	
	private CompressCommandComposite()
	{
		super();
		
	}//constructor
	

	/**
	 * Factory: CompressCommandComposite.<p>
	 * 
	 * @return  CompressCommandComposite new instance
	 */
	
	public static CompressCommandComposite factory()
	{
		return new CompressCommandComposite();
		
	}//method


	@Override
	public CompressCommand getChild( Integer childNumber ) 
	{
		if( _commands.size() < childNumber )
		{
			String msg = "Command element: " 
					+ childNumber 
					+ " not in Command Composite with " 
					+  _commands.size()
					+ " children";
			
			BackupUncheckedException err = new BackupUncheckedException(msg);
			
			err.accept(BackupErrorLoggingVisitor.factory());
			
			throw err;
			
		}//if
		
		return _commands.get(childNumber);
	
	}//method


	@Override
	public ImmutableList<CompressCommand> getChildren() 
	{
		return ImmutableList.copyOf(_commands);
		
	}//method


	@Override
	public CompressCommandComposite add( final CompressCommand commandIn ) 
	{		
		if( _commands.contains(commandIn) )
		{
			final Integer pos = _commands.indexOf(commandIn);
			
			_commands.set(pos, commandIn);
			
		}
		else
		{
			_commands.add(commandIn);
			
		}//if
		
		return this;
		
	}//method
	
	
	@Override
	public CompressCommandComposite add(ImmutableList<?> commandCollection) 
	{
		Iterator<?> commandsIn = commandCollection.iterator();
		
		Integer added = 0;
		
		while( commandsIn.hasNext() )
		{
			added++;
			_commands.add( (CompressCommand) commandsIn.next() );
			
		}//while

		if( added < 1 ) throw new BackupUncheckedException(
				"nothing added to Composite of ImmutableList Commands arg");
		
		return this;
		
	}//method


	/**
	 * Add array of CompressCommand to this Composite.<p>
	 * 
	 * Convenience method: adds type safety; does not require ImmutableList.
	 * <p>
	 * 
	 * @param commandsArray - CompressCommand[]
	 * 
	 * @return  this
	 * 
	 * @see #add(ImmutableList)
	 */
	
	public CompressCommandComposite add( CompressCommand[] commandsArray) 
	{
		return add( ImmutableList.copyOf(commandsArray) );
		
	}//method
	

	@Override
	public CompressCommandComposite remove( final CompressCommand commandIn ) 
	{	
		if( _commands.contains(commandIn) )
		{
			_commands.remove(commandIn);

		}
		else
		{
			String msg = "Command element: " 
					+ commandIn
					+ " not in Command Composite";
			
			BackupUncheckedException err = new BackupUncheckedException(msg);
			
			err.accept(BackupErrorLoggingVisitor.factory());
			
			throw err;
			
		}//if

		return this;
		
	}//method

	
	@Override
	public String toString()
	{
		return "Command Composite size: " 
				+ _commands.size() 
				+ " containing: " 
				+ _commands.toString();
		
	}//method


	@Override
	public ImmutableList<ListenableFuture
							<TaskResults<ImmutableMap<String, String>>>> 
			execute() 
	{		
		final Iterator<CompressCommand> cmds = _commands.iterator();

		while( cmds.hasNext() )
		{
			_futures.add( cmds.next()
							  .execute() );
			
		}//while		
		
		return ImmutableList.copyOf( _futures );
		
	}//method


	@Override
	public Float progress() 
	{
		Float progressTally = 0F;
		
		final Iterator<CompressCommand> cmds = _commands.iterator();

		while( cmds.hasNext() )
		{
			progressTally += cmds.next()
								 .progress();
					
		}//while
		
		return progressTally / _commands.size();
		
	}//method


	@Override
	public Boolean undo() 
	{
		final Iterator<CompressCommand> cmds = _commands.iterator();

		Integer countSuccess = 0;
		
		while( cmds.hasNext() )
		{
			Boolean result = cmds.next()
								 .undo();
			
			if( result ) countSuccess++; 
			
		}//while		
		
		return countSuccess == _commands.size();
			
	}//method


	@Fulfills("CommandProgress")
	@Override
	public String textProgressMeter() 
	{
		return CompressCommand.textProgressMeter("Completed: ", 
				progress(), 
				Integer.valueOf( Settings
									.access()
									.getProperty("PROGRESS-METER-WIDTH"))*2);
		
	}//method


}//class